package cn.dianchiyun.protocol.netty.dtu.handler.impl;

import cn.dianchiyun.protocol.netty.consts.Const;
import cn.dianchiyun.protocol.netty.dtu.entity.DTURead;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.nio.ByteOrder;
import java.util.List;

/**
 * @author 287598457@qq.com
 * @ClassName: DTUDecodeHandler
 * @Description: 解码handler
 * @date 2016年2月29日 下午11:18:20
 */
public class DecodeHandler extends ByteToMessageDecoder {

    private static final Logger logger = LogManager.getLogger("dtu_server");

    /**
     * 剩余长度
     */
    private int remainLength = 0;
    /**
     * 已读长度
     */
    private int hasReadLength = 0;

    public static final int BASE_LENGTH = 4;

    private DTURead dtuRead = new DTURead();

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
        try {
            // 可读长度必须大于基本长度
            if (buffer.readableBytes() >= BASE_LENGTH) {
                // 防止socket字节流攻击
                // 防止，客户端传来的数据过大
                // 因为，太大的数据，是不合理的
                if (buffer.readableBytes() > 2048) {
                    buffer.skipBytes(buffer.readableBytes());
                }

                // 记录包头开始的index
                int beginReader;

                while (true) {
                    // 获取包头开始的index
                    beginReader = buffer.readerIndex();
                    // 标记包头开始的index
                    buffer.markReaderIndex();
                    // 读到了协议的开始标志，结束while循环
                    if (buffer.readByte() == Const.HEAD_DATA) {
                        break;
                    }

                    // 未读到包头，略过一个字节
                    // 每次略过，一个字节，去读取，包头信息的开始标记
                    buffer.resetReaderIndex();
                    buffer.readByte();
                    // 当略过，一个字节之后，
                    // 数据包的长度，又变得不满足
                    // 此时，应该结束。等待后面的数据到达
                    if (buffer.readableBytes() < BASE_LENGTH) {
                        return;
                    }
                }

                ByteBuf buf = buffer.readBytes(6);

                if (buffer.readableBytes() < 4) {
                    // 还原读指针
                    buffer.readerIndex(beginReader);
                    dtuRead = new DTURead("00000000001", true );
                    out.add(dtuRead);
                    return;
                }







                /*// 消息的长度
                int length = buffer.readInt();
                // 判断请求数据包数据是否到齐
                if (buffer.readableBytes() < length) {
                    // 还原读指针
                    buffer.readerIndex(beginReader);
                    return;
                }

                // 读取data数据
                byte[] data = new byte[length];
                buffer.readBytes(data);*/

                //SmartCarProtocol protocol = new SmartCarProtocol(data.length, data);
                //out.add(protocol);
            }


            /*int readableBytes = in.readableBytes();
            if (readableBytes < HEAD_LENGTH) {  //这个HEAD_LENGTH是我们用于表示头长度的字节数。  由于Encoder中我们传的是一个int类型的值，所以这里HEAD_LENGTH的值为4.
                return;
            }
            byte[] bytes = new byte[readableBytes];
            in.readBytes(bytes);
            String receiveStr = HexUtil.receiveHexToString(bytes);//将接收到的数据转为字符串，此字符串就是客户端发送的字符串
            logger.info(receiveStr);
            if(receiveStr.toLowerCase().startsWith("ff10") && receiveStr.toLowerCase().endsWith("ff11")) {
                String serialCode = receiveStr.substring(4, receiveStr.length()-4);
                dtuRead = new DTURead(serialCode, true );
                if(in.isReadable()){
                    System.out.println((char)in.getByte(1));
                }
                // 回收已读字节
                in.discardReadBytes();
                out.add(dtuRead);
            }else{


            }*/




            /*int beginIndex = in.readerIndex();

            String funCode = getFunCodeField(in);

            System.out.println("funCode:"+funCode);
            if ("ff10".equals(funCode)) {//如果是心跳包
                dtuRead.setHeartPack(true);
                //String funCode = getFunCodeField(in);
                //直接转换不行吗
                dtuRead.setSerialCode(new String(in.readBytes(8).array()));
                byte[] end = in.readBytes(2).array();
                out.add(dtuRead);
            } else {
                dtuRead.setFunCode(funCode);
                //dtuRead.setAddress();
                //String funCode = getFunCodeField(in);
                int length = Integer.valueOf(getLengthField(in), 16);
                dtuRead.setLength(length);
                dtuRead.setData(in.readBytes(length).array());
                String end = getCrcField(in);
                out.add(dtuRead);
            }*/
        }catch (Exception e){
            e.printStackTrace();
        }
















       /* int length = in.readInt();






        in.markReaderIndex();        //我们标记一下当前的readIndex的位置
        *//*while (in.isReadable()){
            System.out.println((char)in.readByte());
        }*//*

        String address = getAddressField(in);
        String funCode = getFunCodeField(in);
        String length = getLengthField(in);
        System.out.println(address);
        System.out.println(funCode);
        System.out.println(length);
        String data = getDataField(in, Integer.valueOf(length, 16) * 4);

        System.out.println(data);

        String crc = getCrcField(in);

        DTURead dtuRead = new DTURead();

        out.add(dtuRead);*/
    }

    /**
     * bytebuf 是byte数组的缓冲区
     * 基本功能：
     * 7种java基础类型、byte数组，bytebuffer的读写
     * 缓冲区自身的copy和slice等
     * 设置网络字节序
     * 构造缓冲区实例
     * 操作位置指针等方法
     */
    //@Override
    /*protected void decode1(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        //如果小于报文最小长度，则放弃*********
        // in.clear();
        int readableLength = in.readableBytes();
        if (readableLength < Const.acceptMinMsgLength) {
            return;
        }
        remainLength = readableLength;
        *//**
         * 处理DecoderException异常
         * 原因在indexof 是根据 in.getByte(Index)读的，并不是以readIndex 做参考
         * 这样如果处理上次半包时 获取的headIndex 还是上次信息的索引
         * 导致新发送的信息未被处理 所以netty抛出异常
         *//*
        int markLength = in.readerIndex();
        readableLength = readableLength + markLength;
        hasReadLength = markLength;
        *//**
         * 导致死循环的原因是Netty的IO线程一直会处理到编解码、Pipeline，业务可以在上面定制各种ChannelHandler，如果业务的异常最终抛到最外层，Netty捕获后继续处理，如果再次进入业务异常，再次抛出捕获，在这种场景下可能会导致死循环；
         另外，如果Channel处理不当，也会导致死循环，这个跟NIO的多路复用器相关，例如如果一个损坏的Channel没有被注销和关闭，它就会不断被多路复用器选择就绪，这样就会导致死循环；
         *//*
        while (checkByteBuf(in, readableLength)) {
            byte head = getHeadField(in);
            byte type = getTypeField(in);
            int msgLength = getMsgLengthField(in);
            String identity = getIdentityField(in);
            //请求和注销及心跳是没有数据内容和校验的 无数据内容无校验
            byte[] data = {};
            byte[] crc = {};
            if (msgLength != Const.acceptMinMsgLength) {
                data = getDataField(in, msgLength);
                crc = getCrcField(in, data);
                if (crc == null) {
                    return;
                }
            }
            byte tail = getTailField(in);
            DTUMsg dtuMsg = new DTUMsg(head, type, msgLength, identity, data, crc, tail);
            out.add(dtuMsg);
        }
    }*/


    /**
     * @param //in
     * @param //readableLength
     * @return boolean
     * @Title. checkByteBuf
     * @Description. 处理半包和粘包
     * @exception.
     */
   /* private boolean checkByteBuf(ByteBuf in, int readableLength) {
        if (remainLength < Const.acceptMinMsgLength) {
            return false;
        }
        int headIndex = in.indexOf(hasReadLength, readableLength, Const.headDefault);
        if (headIndex < 0) {
            return false;
        }
        in.readerIndex(headIndex);
        byte head = getHeadField(in);
        byte type = getTypeField(in);
        while (type == Const.headDefault) {
            headIndex = headIndex + 1;
            type = getTypeField(in);
        }
        if (!ArrayUtils.contains(Const.reqs, type)) {
            logger.error("类型不正确");
            hasReadLength = headIndex + 1;
            return checkByteBuf(in, readableLength);
        }
        int msgLength = getMsgLengthField(in);
        int needLength = msgLength + headIndex;
        if (needLength > readableLength) {
            return false;
        }
        byte tail = in.getByte(needLength - 1);
        if (tail != Const.tailDefault) {
            logger.error("帧尾不正确");
            hasReadLength = headIndex + 1;
            return checkByteBuf(in, readableLength);
        }
        in.readerIndex(headIndex);
        remainLength = readableLength - needLength;
        hasReadLength = needLength;
        return true;
    }*/


    private DTURead convertToObject(byte[] body) {
        String read = new String(body);
        System.out.println(read);
        return new DTURead();
    }

    private String getAddressField(ByteBuf in) {
        //从readerIndex开始获取字节值，readerIndex增加1
        ByteBuf buf = in.readBytes(Const.addressFieldLength);
        hasReadLength += Const.addressFieldLength;
        //得到一个字节在指定绝对指数在这个缓冲区。这种方法不修改readerIndex或writerIndex的缓冲区。
        //获取数组
        //返回这个缓冲区的字节数组
        byte[] res = buf.array();
        //直接转换不行吗
        return new String(res);
    }

    private String getFunCodeField(ByteBuf in) {
        //从readerIndex开始获取字节值，readerIndex增加1
        ByteBuf buf = in.readBytes(Const.funCodeFieldLength);
        hasReadLength += Const.funCodeFieldLength;
        //得到一个字节在指定绝对指数在这个缓冲区。这种方法不修改readerIndex或writerIndex的缓冲区。
        //获取数组
        //返回这个缓冲区的字节数组
        byte[] res = buf.array();
        //直接转换不行吗
        return new String(res);
    }

    private String getLengthField(ByteBuf in) {
        //从readerIndex开始获取字节值，readerIndex增加1
        ByteBuf buf = in.readBytes(Const.lengthFieldLength);
        hasReadLength += Const.lengthFieldLength;
        //得到一个字节在指定绝对指数在这个缓冲区。这种方法不修改readerIndex或writerIndex的缓冲区。
        //获取数组
        //返回这个缓冲区的字节数组
        byte[] res = buf.array();
        //直接转换不行吗
        return new String(res);
    }

    private String getDataField(ByteBuf in, int dataLength) {
        ByteBuf buf = in.readBytes(dataLength);
        hasReadLength += dataLength;
        byte[] res = buf.array();
        return new String(res);
    }

    private String getCrcField(ByteBuf in) {
        ByteBuf buf = in.readBytes(Const.crcFieldLength);
        hasReadLength += Const.crcFieldLength;
        byte[] res = buf.array();
        return new String(res);
    }

    private byte[] getCrcField(ByteBuf in, byte[] data) {
        ByteBuf buf = in.readBytes(Const.crcFieldLength);
        buf.order(ByteOrder.BIG_ENDIAN);
        //crc校验
        /*if (data.length != 0 && CRC16Util.crc16(data) == ByteUtil.bytesToInt(buf.array())) {
            return buf.array();
        }*/
        return null;
    }

    private void resetBytebufReadIndex(ByteBuf bytebuf, int readIndex) {
        bytebuf.readerIndex(readIndex);

    }

}
